package com.castle.fortress.admin.study.course.controller;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.castle.fortress.admin.core.constants.GlobalConstants;
import com.castle.fortress.admin.member.dto.MemberDto;
import com.castle.fortress.admin.member.entity.MemberEntity;
import com.castle.fortress.admin.member.service.MemberService;
import com.castle.fortress.admin.study.course.entity.StudyCourseLogEntity;
import com.castle.fortress.admin.study.course.dto.StudyCourseLogDto;
import com.castle.fortress.admin.study.course.entity.vo.MemberStudyTimeRankVO;
import com.castle.fortress.admin.study.course.service.StudyCourseLogService;
import com.castle.fortress.admin.utils.RedisUtils;
import com.castle.fortress.admin.utils.WebUtil;
import com.castle.fortress.common.utils.ConvertUtil;
import com.castle.fortress.common.entity.RespBody;
import com.castle.fortress.common.exception.BizException;
import com.castle.fortress.common.respcode.GlobalRespCode;
import com.castle.fortress.common.enums.OperationTypeEnum;
import com.castle.fortress.admin.core.annotation.CastleLog;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import springfox.documentation.annotations.ApiIgnore;
import org.apache.logging.log4j.util.Strings;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.stereotype.Controller;
import com.castle.fortress.admin.utils.ExcelUtils;

import javax.servlet.http.HttpServletResponse;

import com.castle.fortress.common.entity.DynamicExcelEntity;

import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * 课程记录表 控制器
 *
 * @author castle
 * @since 2023-02-14
 */
@Api(tags = "课程记录表管理控制器")
@Controller
@Slf4j
public class StudyCourseLogController {
    @Autowired
    private StudyCourseLogService studyCourseLogService;
    @Autowired
    private MemberService memberService;
    @Autowired
    private RedisUtils redisUtils;

    /**
     * 课程记录表的分页展示
     *
     * @param studyCourseLogDto 课程记录表实体类
     * @param current           当前页
     * @param size              每页记录数
     * @return
     */
    @CastleLog(operLocation = "课程记录表", operType = OperationTypeEnum.QUERY)
    @ApiOperation("课程记录表分页展示")
    @GetMapping("/study/course/studyCourseLog/page")
    @ResponseBody
    @RequiresPermissions("study:course:studyCourseLog:pageList")
    public RespBody<IPage<StudyCourseLogDto>> pageStudyCourseLog(StudyCourseLogDto studyCourseLogDto, @RequestParam(required = false) Integer current, @RequestParam(required = false) Integer size) {
        Integer pageIndex = current == null ? GlobalConstants.DEFAULT_PAGE_INDEX : current;
        Integer pageSize = size == null ? GlobalConstants.DEFAULT_PAGE_SIZE : size;
        Page<StudyCourseLogDto> page = new Page(pageIndex, pageSize);
        IPage<StudyCourseLogDto> pages = studyCourseLogService.pageStudyCourseLog(page, studyCourseLogDto);

        return RespBody.data(pages);
    }

    /**
     * 课程记录表的列表展示
     *
     * @param studyCourseLogDto 课程记录表实体类
     * @return
     */
    @CastleLog(operLocation = "课程记录表", operType = OperationTypeEnum.QUERY)
    @ApiOperation("课程记录表列表展示")
    @GetMapping("/study/course/studyCourseLog/list")
    @ResponseBody
    public RespBody<List<StudyCourseLogDto>> listStudyCourseLog(StudyCourseLogDto studyCourseLogDto) {
        List<StudyCourseLogDto> list = studyCourseLogService.listStudyCourseLog(studyCourseLogDto);
        return RespBody.data(list);
    }

    /**
     * 课程记录表保存
     *
     * @param studyCourseLogDto 课程记录表实体类
     * @return
     */
    @CastleLog(operLocation = "课程记录表", operType = OperationTypeEnum.INSERT)
    @ApiOperation("课程记录表保存")
    @PostMapping("/study/course/studyCourseLog/save")
    @ResponseBody
    @RequiresPermissions("study:course:studyCourseLog:save")
    public RespBody<String> saveStudyCourseLog(@RequestBody StudyCourseLogDto studyCourseLogDto) {
        if (studyCourseLogDto == null) {
            throw new BizException(GlobalRespCode.PARAM_MISSED);
        }
        StudyCourseLogEntity studyCourseLogEntity = ConvertUtil.transformObj(studyCourseLogDto, StudyCourseLogEntity.class);
        if (studyCourseLogService.save(studyCourseLogEntity)) {
            return RespBody.data("保存成功");
        } else {
            return RespBody.fail(GlobalRespCode.OPERATE_ERROR);
        }
    }

    /**
     * 课程记录表编辑
     *
     * @param studyCourseLogDto 课程记录表实体类
     * @return
     */
    @CastleLog(operLocation = "课程记录表", operType = OperationTypeEnum.UPDATE)
    @ApiOperation("课程记录表编辑")
    @PostMapping("/study/course/studyCourseLog/edit")
    @ResponseBody
    @RequiresPermissions("study:course:studyCourseLog:edit")
    public RespBody<String> updateStudyCourseLog(@RequestBody StudyCourseLogDto studyCourseLogDto) {
        if (studyCourseLogDto == null || studyCourseLogDto.getId() == null || studyCourseLogDto.getId().equals(0L)) {
            throw new BizException(GlobalRespCode.PARAM_MISSED);
        }
        StudyCourseLogEntity studyCourseLogEntity = ConvertUtil.transformObj(studyCourseLogDto, StudyCourseLogEntity.class);
        if (studyCourseLogService.updateById(studyCourseLogEntity)) {
            return RespBody.data("保存成功");
        } else {
            return RespBody.fail(GlobalRespCode.OPERATE_ERROR);
        }
    }

    /**
     * 课程记录表删除
     *
     * @param id
     * @return
     */
    @CastleLog(operLocation = "课程记录表", operType = OperationTypeEnum.DELETE)
    @ApiOperation("课程记录表删除")
    @PostMapping("/study/course/studyCourseLog/delete")
    @ResponseBody
    @RequiresPermissions("study:course:studyCourseLog:delete")
    public RespBody<String> deleteStudyCourseLog(@RequestParam Long id) {
        if (id == null) {
            throw new BizException(GlobalRespCode.PARAM_MISSED);
        }
        if (studyCourseLogService.removeById(id)) {
            return RespBody.data("保存成功");
        } else {
            return RespBody.fail(GlobalRespCode.OPERATE_ERROR);
        }
    }


    /**
     * 课程记录表批量删除
     *
     * @param ids
     * @return
     */
    @CastleLog(operLocation = "课程记录表", operType = OperationTypeEnum.DELETE)
    @ApiOperation("课程记录表批量删除")
    @PostMapping("/study/course/studyCourseLog/deleteBatch")
    @ResponseBody
    @RequiresPermissions("study:course:studyCourseLog:deleteBatch")
    public RespBody<String> deleteStudyCourseLogBatch(@RequestBody List<Long> ids) {
        if (ids == null || ids.isEmpty()) {
            throw new BizException(GlobalRespCode.PARAM_MISSED);
        }
        if (studyCourseLogService.removeByIds(ids)) {
            return RespBody.data("保存成功");
        } else {
            return RespBody.fail(GlobalRespCode.OPERATE_ERROR);
        }
    }

    /**
     * 课程记录表详情
     *
     * @param id 课程记录表id
     * @return
     */
    @CastleLog(operLocation = "课程记录表", operType = OperationTypeEnum.QUERY)
    @ApiOperation("课程记录表详情")
    @GetMapping("/study/course/studyCourseLog/info")
    @ResponseBody
    @RequiresPermissions("study:course:studyCourseLog:info")
    public RespBody<StudyCourseLogDto> infoStudyCourseLog(@RequestParam Long id) {
        if (id == null) {
            throw new BizException(GlobalRespCode.PARAM_MISSED);
        }
        StudyCourseLogEntity studyCourseLogEntity = studyCourseLogService.getById(id);
        StudyCourseLogDto studyCourseLogDto = ConvertUtil.transformObj(studyCourseLogEntity, StudyCourseLogDto.class);

        return RespBody.data(studyCourseLogDto);
    }

    /**
     * 动态表头导出 依据展示的字段导出对应报表
     *
     * @param dynamicExcelEntity
     * @param response
     * @throws Exception
     */
    @CastleLog(operLocation = "课程记录表", operType = OperationTypeEnum.EXPORT)
    @PostMapping("/study/course/studyCourseLog/exportDynamic")
    @ApiOperation("动态表头导出，依据展示的字段导出对应报表")
    public void exportDynamic(@RequestBody DynamicExcelEntity<StudyCourseLogDto> dynamicExcelEntity, HttpServletResponse response) throws Exception {
        List<StudyCourseLogDto> list = studyCourseLogService.listStudyCourseLog(dynamicExcelEntity.getDto());
        // 字典、枚举、接口、json常量等转义后的列表数据 根据实际情况初始化该对象，null为list数据直接导出
        List<List<Object>> dataList = null;
		/*
         根据实际情况初始化dataList,可参照 com.castle.fortress.admin.system.controller.TmpDemoController类中方法：exportDynamic
        */
        ExcelUtils.exportDynamic(response, dynamicExcelEntity.getFileName() + ".xlsx", null, list, dynamicExcelEntity.getHeaderList(), dataList);
    }


    private final HashMap<String, Integer> unitMap = new HashMap<String, Integer>() {{
        put("day", 1);
        put("week", 2);
        put("month", 3);
        put("year", 4);
    }};

    @CastleLog(operLocation = "刷新排名缓存", operType = OperationTypeEnum.QUERY)
    @ApiOperation("刷新Redis缓存")
    @GetMapping("/study/course/studyCourseLog/rank/flush/{unit}")
    @ResponseBody
    public RespBody<Object> flushRank(@PathVariable("unit") String unit) {
        final String redisKey = "course:log:rank:" + unit + ":";
        redisUtils.remove(redisKey);
        return RespBody.data("刷新成功");
    }

    /**
     * 1为天/2为周/3为月
     */
    @CastleLog(operLocation = "课程排名统计", operType = OperationTypeEnum.QUERY)
    @GetMapping("/study/course/studyCourseLog/rank/{unit}")
    @ApiOperation("获取全部排名")
    @ResponseBody
    public RespBody getRankByUnit(@PathVariable("unit") String unit) {
        MemberDto memberDto = WebUtil.currentMember();
        Long id = memberDto.getId();
        log.info("获取全部排名 unit-> {}", unit);
        Integer val;
        if (StrUtil.isEmpty(unit) || (val = unitMap.get(unit)) == null) {
            return RespBody.fail(GlobalRespCode.PARAM_MISSED);
        }
        final String redisKey = "course:log:rank:" + unit + ":";
        log.info("尝试走缓存 redisKey-> {}", redisKey);

        String cashedResult = null;
        Object o = redisUtils.get(redisKey);
        if (o != null) {
            cashedResult = o.toString();
        }

        if (StrUtil.isNotEmpty(cashedResult)) {
            log.info("走缓存成功 cashedResult-> {}", cashedResult);
            RespBody<Object> data = RespBody.data(o);
            log.info("返回结果 data-> {}", data);
            return data;
        }

        List<StudyCourseLogEntity> entities = studyCourseLogService.listStudyCourseLogByTimeUnit(val);

        Map<Long, Long> resMap = new HashMap<>();
        entities.forEach(ety -> {
            if (ety.getMemberId() == null) {
                return;
            }
            Long memberId = ety.getMemberId();
            Integer studyTime = ety.getStudyTime();
            if (studyTime == null || studyTime <= 0) {
                return;
            }
            resMap.put(memberId, resMap.getOrDefault(memberId, 0L) + studyTime);
        });

        List<MemberStudyTimeRankVO> res = new ArrayList<>();
        resMap.keySet().forEach(key -> {
            MemberStudyTimeRankVO vo = new MemberStudyTimeRankVO();
            MemberEntity dto = memberService.getById(key);
            if (dto != null) {
                vo.setNickName(dto.getNickName());
                vo.setAvatar(dto.getAvatar());
                Long time = resMap.get(key);
                vo.setStudyTimeSeconds(time);
                // 转成 小时 分钟 秒
                long hour = time / 3600;
                long minute = (time - hour * 3600) / 60;
                long second = time - hour * 3600 - minute * 60;
                String timeString = "";
                if(hour > 0) {
                    timeString += hour + "小时";
                }
                if(minute > 0) {
                    timeString += minute + "分";
                }
                if(second > 0) {
                    timeString += second + "秒";
                }
                vo.setMe(false);
                vo.setStudyTime(timeString);
                if(dto.getId().equals(id)) {
                    vo.setMe(true);
                }
                res.add(vo);
            }
        });

        // 倒序
        res.sort((o1, o2) -> o2.getStudyTimeSeconds().compareTo(o1.getStudyTimeSeconds()));

        // 存入redis，2小时过期
        redisUtils.set(redisKey, JSON.toJSON(res), 2L, TimeUnit.HOURS);
        String jsonStr = JSONUtil.toJsonStr(res);
        log.info("获取排名成功" + jsonStr);
        return RespBody.data(res);
    }
}
